package com.atom.module.document.utils;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.Point;

import com.atom.module.logger.Logger;
import com.huawei.hmf.tasks.Task;
import com.huawei.hms.mlsdk.common.MLFrame;
import com.huawei.hms.mlsdk.dsc.MLDocumentSkewCorrectionAnalyzer;
import com.huawei.hms.mlsdk.dsc.MLDocumentSkewCorrectionAnalyzerFactory;
import com.huawei.hms.mlsdk.dsc.MLDocumentSkewCorrectionAnalyzerSetting;
import com.huawei.hms.mlsdk.dsc.MLDocumentSkewCorrectionCoordinateInput;
import com.huawei.hms.mlsdk.dsc.MLDocumentSkewCorrectionResult;
import com.huawei.hms.mlsdk.dsc.MLDocumentSkewDetectResult;

import java.util.Arrays;

import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;

/**
 * All rights Reserved, Designed By www.rongdasoft.com
 *
 * @version V1.0
 * @Title: DocumentUtils
 * @Description:
 * @author: wuyongzheng
 * @date: 2022/3/25
 * @Copyright: 2022/3/25 www.rongdasoft.com Inc. All rights reserved.
 */
public class DocumentUtils {
    // document
    private static MLDocumentSkewCorrectionAnalyzer analyzer;

    private static MLFrame.Property property;

    public static MLDocumentSkewCorrectionAnalyzer getMLDocAnalyzer() {
        if (analyzer == null) {
            analyzer = MLDocumentSkewCorrectionAnalyzerFactory.getInstance()
                .getDocumentSkewCorrectionAnalyzer(
                    new MLDocumentSkewCorrectionAnalyzerSetting.Factory().create());
        }
        return analyzer;
    }

    private static MLFrame.Property getMLFrameProperty(int w, int h, boolean isFront) {
        if (property == null || (property.getWidth() != w || property.getHeight() != h || property.getQuadrant() != (isFront ? 0 : 1))) {
            property = new MLFrame.Property.Creator()
                .setFormatType(ImageFormat.NV21)
                .setWidth(w)
                .setHeight(h)
                .setQuadrant(isFront ? 0 : 1)
                .create();
        }
        return property;
    }

    private static MLFrame createFrame(byte[] array, int w, int h, boolean isFront) {
        return MLFrame.fromByteArray(
            array,
            getMLFrameProperty(w, h, isFront)
        );
    }

    public static MLFrame createFrame(Bitmap bitmap) {
        return MLFrame.fromBitmap(bitmap);
    }

    public static void analysisArray(MLFrame frame, Observer<Point[]> callback) {
        Observable.create((ObservableOnSubscribe<Point[]>) emitter -> {
            Task<MLDocumentSkewDetectResult> tasks =
                getMLDocAnalyzer().asyncDocumentSkewDetect(frame);
            if (tasks != null) {
                tasks.addOnSuccessListener(mlDocumentSkewDetectResult -> {
                    if (mlDocumentSkewDetectResult.getResultCode() == 0) {
                        Point leftTop = mlDocumentSkewDetectResult.getLeftTopPosition();
                        Point rightTop = mlDocumentSkewDetectResult.getRightTopPosition();
                        Point leftBottom = mlDocumentSkewDetectResult.getLeftBottomPosition();
                        Point rightBottom = mlDocumentSkewDetectResult.getRightBottomPosition();
                        Point[] points = new Point[]{leftTop, rightTop, rightBottom, leftBottom};
                        emitter.onNext(points);
                        emitter.onComplete();
                    } else {
                        emitter.onError(new Exception("result code = " + mlDocumentSkewDetectResult.getResultCode()));
                    }
                }).addOnFailureListener(emitter::onError);
                return;
            }
            emitter.onError(new Exception("points is empty"));
        })
            .subscribeOn(Schedulers.computation())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(callback);
    }

    public static void shearBitmapByPoints(MLFrame frame, Point[] points, Observer<Bitmap> observer) {
        Observable.create((ObservableOnSubscribe<Bitmap>) emitter -> {
            MLDocumentSkewCorrectionCoordinateInput coordinateData = new MLDocumentSkewCorrectionCoordinateInput(Arrays.asList(points));
            Task<MLDocumentSkewCorrectionResult> tasks = null;
            try {
                tasks = getMLDocAnalyzer().asyncDocumentSkewCorrect(frame, coordinateData);
            } catch (Exception e) {
                Logger.e(e);
            }
            if (tasks != null) {
                tasks.addOnSuccessListener(mlDocumentSkewDetectResult -> {
                    if (mlDocumentSkewDetectResult != null &&
                        mlDocumentSkewDetectResult.getResultCode() == 0) {
                        emitter.onNext(mlDocumentSkewDetectResult.getCorrected());
                        emitter.onComplete();
                    }
                }).addOnFailureListener(emitter::onError);
                return;
            }
            emitter.onError(new Exception("point is not shear bitmap" + Arrays.asList(points)));
        }).subscribeOn(Schedulers.single())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(observer);
    }

    public static Observable<Bitmap> shearBitmapByPoints2(MLFrame frame, Point[] points) {
        return Observable.create(emitter -> {
            Logger.e("saveDocumentInLocal 0");
            MLDocumentSkewCorrectionCoordinateInput coordinateData = new MLDocumentSkewCorrectionCoordinateInput(Arrays.asList(points));
            Task<MLDocumentSkewCorrectionResult> tasks = null;
            try {
                tasks = getMLDocAnalyzer().asyncDocumentSkewCorrect(frame, coordinateData);
            } catch (Exception e) {
                Logger.e(e);
            }
            if (tasks != null) {
                tasks.addOnSuccessListener(mlDocumentSkewDetectResult -> {
                    if (mlDocumentSkewDetectResult != null &&
                        mlDocumentSkewDetectResult.getResultCode() == 0) {
                        emitter.onNext(mlDocumentSkewDetectResult.getCorrected());
                        emitter.onComplete();
                    }
                }).addOnFailureListener(emitter::onError);
                return;
            }
            emitter.onError(new Exception("point is not shear bitmap" + Arrays.asList(points)));
        });
    }

    @SuppressLint("CheckResult")
    public static Observable<Point[]> analysisArray(byte[] array, int w, int h, boolean isFront) {
        return Observable.create((ObservableOnSubscribe<Point[]>) emitter -> {
            Task<MLDocumentSkewDetectResult> tasks =
                getMLDocAnalyzer().asyncDocumentSkewDetect(createFrame(array, w, h, isFront));
            if (tasks != null) {
                tasks.addOnSuccessListener(mlDocumentSkewDetectResult -> {
                    if (mlDocumentSkewDetectResult.getResultCode() == 0) {
                        // Recognition success.
                        Point leftTop = mlDocumentSkewDetectResult.getLeftTopPosition();
                        Point rightTop = mlDocumentSkewDetectResult.getRightTopPosition();
                        Point leftBottom = mlDocumentSkewDetectResult.getLeftBottomPosition();
                        Point rightBottom = mlDocumentSkewDetectResult.getRightBottomPosition();
                        emitter.onNext(new Point[]{leftTop, rightTop, rightBottom, leftBottom});
                        emitter.onComplete();
                        return;
                    }
                    emitter.onError(new Exception("getResultCode != 0"));
                }).addOnFailureListener(emitter::onError);
                return;
            }
            emitter.onError(new Exception("empty points"));
        }).subscribeOn(Schedulers.single())
            .observeOn(AndroidSchedulers.mainThread());
    }
}
